package cn.qn.flowable.controller;

import cn.hutool.core.util.ObjectUtil;
import cn.qn.flowable.result.BaseResponse;
import cn.qn.flowable.vo.BpmTaskApproveReqVO;
import cn.qn.flowable.vo.BpmTaskRejectReqVO;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.Process;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.*;

@RestController
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class TaskController {
    private final TaskService taskService;
    private final RepositoryService repositoryService;
    private final HistoryService historyService;
    private final RuntimeService runtimeService;

    public final String TASK_VARIABLE_REASON = "TASK_REASON";
    /**
     * 完成节点任务.
     *
     * @return 流程key
     */
    @PostMapping("/complete")
    public ResponseEntity<Object> completeTask(@Valid @RequestBody BpmTaskApproveReqVO reqVO) {
        // 审批意见
        taskService.setVariableLocal(reqVO.getId(), TASK_VARIABLE_REASON, reqVO.getReason());
        //1.完成指定任务的审批
        taskService.complete(reqVO.getId(), reqVO.getVariables());

        //2.返回响应
        return new ResponseEntity<>(new BaseResponse<>(null), HttpStatus.OK);
    }

    @PutMapping("/reject")
    @Operation(summary = "不通过任务")
    public ResponseEntity<BaseResponse> rejectTask(@Valid @RequestBody BpmTaskRejectReqVO reqVO) {
        //1.查询目标任务数据
        HistoricTaskInstance targetTask = historyService
                .createHistoricTaskInstanceQuery()
                .taskId(reqVO.getId())
                .singleResult();
        //2.判空
        if (Objects.isNull(targetTask)) {
            return new ResponseEntity<>(new BaseResponse<>(BaseResponse.FAIL, "驳回目的任务不存在！", null),
                    HttpStatus.BAD_REQUEST);
        }

        //3.获取流程实例id
        String processInstanceId = targetTask.getProcessInstanceId();
        //4.获取当前任务节点
        Task sourceTask = taskService.createTaskQuery()
                .processInstanceId(processInstanceId)
                .active()
                .singleResult();
        //5.判空
        if (Objects.isNull(sourceTask)) {
            return new ResponseEntity<>(new BaseResponse<>(BaseResponse.FAIL, "驳回当前任务不存在！"),
                    HttpStatus.BAD_REQUEST);
        }
        //6.不能驳回到自己
        if (Objects.equals(targetTask, sourceTask)) {
            return new ResponseEntity<>(new BaseResponse<>(BaseResponse.FAIL, "不能驳回到当前节点！"),
                    HttpStatus.BAD_REQUEST);
        }
        //7.获取流程定义id
        String processDefinitionId = targetTask.getProcessDefinitionId();
        //8.使用流程定义id查询流程定义，获取流程定义的所有节点信息
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
        Process process = bpmnModel.getProcesses().getFirst();
        Collection<FlowElement> flowElements = process.getFlowElements();
        //9.遍历流程定义的所有节点，找到目标节点的定义信息
        FlowElement targetElement = null;
        for (FlowElement flowElement : flowElements) {
            if (ObjectUtil.equal(flowElement.getId(), targetTask.getTaskDefinitionKey())) {
                targetElement = flowElement;
                break;
            }
        }
        //10.判空
        if (Objects.isNull(targetElement)) {
            return new ResponseEntity<>(new BaseResponse<>(BaseResponse.FAIL, "驳回目的任务定义不存在！"),
                    HttpStatus.BAD_REQUEST);
        }

        // 审批意见
        taskService.setVariableLocal(reqVO.getId(), TASK_VARIABLE_REASON, reqVO.getReason());
        Task rollbackTask = rollbackTask(sourceTask, targetTask);

        return new ResponseEntity<>(new BaseResponse<>(true), HttpStatus.OK);
    }

    /**
     * 回退节点并清理数据.
     *
     * @param sourceTask 当前任务
     * @param targetTask 目标任务
     * @return 驳回后的任务
     */
    public Task rollbackTask(Task sourceTask, HistoricTaskInstance targetTask) {
        //1.获取流程实例id
        String processInstanceId = sourceTask.getProcessInstanceId();
        //2.回退节点
        runtimeService.createChangeActivityStateBuilder()
                //指定流程实例ID
                .processInstanceId(processInstanceId)
                //指定源节点和目标节点
                .moveActivityIdTo(sourceTask.getTaskDefinitionKey(), targetTask.getTaskDefinitionKey())
                .changeState();
        //3.清理两个节点之间的脏数据
        clearDirtyData(processInstanceId, sourceTask, targetTask);
        //4.返回新目标节点
        return taskService.createTaskQuery()
                //指定流程实例ID
                .processInstanceId(processInstanceId)
                //指定目标节点定义
                .taskDefinitionKey(targetTask.getTaskDefinitionKey())
                .singleResult();
    }

    /**
     * 清除节点脏数据.
     *
     * @param processInstanceId 流程实例id
     * @param sourceTask        当前任务id
     * @param targetTask        目标任务id
     */
    private void clearDirtyData(String processInstanceId, Task sourceTask,
                                HistoricTaskInstance targetTask) {
        //1.获取历史任务列表
        List<HistoricTaskInstance> historyTasks = historyService
                .createHistoricTaskInstanceQuery()
                .processInstanceId(processInstanceId)
                //按照任务开始时间降序排序
                .orderByHistoricTaskInstanceStartTime().desc()
                .list();
        //2.获取sourceTaskId和targetTaskId之间的所有任务节点，包括 targetTask 和 sourceTask，回滚之后删除。
        List<HistoricTaskInstance> betweenNodes =
                getBetweenNodes(historyTasks, sourceTask.getId(), targetTask.getId());
        //3.获取节点id列表
        List<String> taskIds = betweenNodes.stream()
                .map(HistoricTaskInstance::getId)
                .toList();
        //4.删除流程变量
        List<String> variableKeys = new ArrayList<>();
        for (String taskId : taskIds) {
            //分数key
            String scoreKey = "score";
            //所属人key
            String ownerKey = "ownerId";
            variableKeys.add(scoreKey);
            variableKeys.add(ownerKey);
            //删除历史节点
            historyService.deleteHistoricTaskInstance(taskId);
        }
        //5.删除流程实例中的指定变量
        runtimeService.removeVariables(processInstanceId, variableKeys);
    }

    /**
     * 获取sourceTaskId和targetTaskId之间的所有任务节点,包括 targetTask 和 sourceTask.
     *
     * @param sourceTaskId 当前任务id
     * @param targetTaskId 目标任务id
     * @return 任务节点列表
     */
    List<HistoricTaskInstance> getBetweenNodes(List<HistoricTaskInstance> historicTasks,
                                               String sourceTaskId,
                                               String targetTaskId) {
        //1.初始化一个列表，用于存储源任务和目标任务之间的所有任务节点
        List<HistoricTaskInstance> betweenNodes = new ArrayList<>();
        //2.定义一个标志，用于判断是否开始记录任务节点
        boolean startRecord = false;
        for (HistoricTaskInstance historicTask : historicTasks) {
            //3.如果历史任务的ID等于源任务的ID，那么开始记录任务节点
            if (ObjectUtil.equal(historicTask.getId(), sourceTaskId)) {
                startRecord = true;
            }
            //4.如果开始记录任务节点，那么将当前历史任务添加到任务节点列表中
            if (startRecord) {
                betweenNodes.add(historicTask);
            }
            //5.如果历史任务的ID等于目标任务的ID，那么停止记录任务节点，并结束循环
            if (ObjectUtil.equal(historicTask.getId(), targetTaskId)) {
                break;
            }
        }
        //6.返回任务节点列表
        return betweenNodes;
    }

    /**
     * 查询指定用户的待办任务列表
     */
    @GetMapping("/task/todo/list/{userId}")
    public ResponseEntity<BaseResponse<List<Map<String, Object>>>> getTodoTaskList(@PathVariable String userId) {
        //待办任务集合（带分页）
        List<Task> list = taskService.createTaskQuery()
                .taskAssignee(userId)
                .active()
                .orderByTaskCreateTime()
                .desc()
                .listPage(1, 10);

        List<Map<String, Object>> result = list.stream().map(task -> {
            Map<String, Object> map = new HashMap<>();
            map.put("id", task.getId());
            map.put("name", task.getName());
            map.put("assignee", task.getAssignee());
            map.put("createTime", task.getCreateTime());
            map.put("processInstanceId", task.getProcessInstanceId());
            map.put("processDefinitionId", task.getProcessDefinitionId());
            return map;
        }).toList();
        return new ResponseEntity<>(new BaseResponse<>(result), HttpStatus.OK);
    }

    @GetMapping("done-page/{userId}")
    @Operation(summary = "获取 Done 已办任务分页")
    public ResponseEntity<BaseResponse<List<Map<String, Object>>>>  getTaskDonePage(@PathVariable String userId) {
        List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery()
                .finished() // 已完成
                .taskAssignee(String.valueOf(userId)) // 分配给自己
                .includeTaskLocalVariables()
                .orderByHistoricTaskInstanceEndTime().desc()// 审批时间倒序
                .listPage(1, 10);

        List<Map<String, Object>> result = list.stream().map(task -> {
            Map<String, Object> map = new HashMap<>();
            map.put("id", task.getId());
            map.put("name", task.getName());
            map.put("assignee", task.getAssignee());
            map.put("createTime", task.getCreateTime());
            map.put("processInstanceId", task.getProcessInstanceId());
            map.put("processDefinitionId", task.getProcessDefinitionId());
            Map<String, Object> variablesLocal = task.getTaskLocalVariables();
            map.put("reason", variablesLocal.getOrDefault(TASK_VARIABLE_REASON, ""));
            return map;
        }).toList();
        return new ResponseEntity<>(new BaseResponse<>(result), HttpStatus.OK);
    }

}
